22/09/2024 - 28/09/2024

25/09/2024 04:36

I established a faster "python" midas frontend by instead having a python thread put it's data in a shared memory buffer that the C++ script reads:

import mmap
import posix_ipc
import numpy as np

SHM_NAME = "/my_shared_memory"
SEM_NAME = "/my_semaphore"
DATA_SIZE = 1024 * 1024  # 1 MB

# Create or open shared memory
shared_mem = posix_ipc.SharedMemory(SHM_NAME, posix_ipc.O_CREAT, size=DATA_SIZE)
memory = mmap.mmap(shared_mem.fd, shared_mem.size)
shared_mem.close_fd()

# Create or open semaphore
semaphore = posix_ipc.Semaphore(SEM_NAME, posix_ipc.O_CREAT, initial_value=0)

try:
    while True:
        # Generate or retrieve data
        data = np.random.randint(0, 65535, size=DATA_SIZE // 2, dtype=np.uint16)
        byte_data = data.tobytes()

        # Write data to shared memory
        memory.seek(0)
        memory.write(byte_data)

        # Signal the C++ frontend
        semaphore.release()

        # Control the data generation rate as needed
except KeyboardInterrupt:
    pass
finally:
    # Clean up
    memory.close()
    shared_mem.unlink()
    semaphore.unlink()
import mmap
import posix_ipc
import numpy as np

SHM_NAME = "/my_shared_memory"
SEM_NAME = "/my_semaphore"
DATA_SIZE = 1024 * 1024  # 1 MB

# Create or open shared memory
shared_mem = posix_ipc.SharedMemory(SHM_NAME, posix_ipc.O_CREAT, size=DATA_SIZE)
memory = mmap.mmap(shared_mem.fd, shared_mem.size)
shared_mem.close_fd()

# Create or open semaphore
semaphore = posix_ipc.Semaphore(SEM_NAME, posix_ipc.O_CREAT, initial_value=0)

try:
    while True:
        # Generate or retrieve data
        data = np.random.randint(0, 65535, size=DATA_SIZE // 2, dtype=np.uint16)
        byte_data = data.tobytes()

        # Write data to shared memory
        memory.seek(0)
        memory.write(byte_data)

        # Signal the C++ frontend
        semaphore.release()

        # Control the data generation rate as needed
except KeyboardInterrupt:
    pass
finally:
    # Clean up
    memory.close()
    shared_mem.unlink()
    semaphore.unlink()

Then the midas frontend is out standard C++ frontend, to eliminate the python slowdown we were seeing:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include "midas.h"
#include "mfe.h"
#include <curl/curl.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <semaphore.h>
#include <chrono>
#include <errno.h>

void trigger_update(INT, INT, void*);

/*-- Globals -------------------------------------------------------*/

/* The frontend name (client name) as seen by other MIDAS clients */
const char *frontend_name = "DataSimulator";
/* The frontend file name, don't change it */
const char *frontend_file_name = __FILE__;

/* frontend_loop is called periodically if this variable is TRUE */
BOOL frontend_call_loop = FALSE;

/* a frontend status page is displayed with this frequency in ms */
INT display_period = 0;

/* maximum event size produced by this frontend */
INT max_event_size = 1024 * 1024 * 32;

/* maximum event size for fragmented events (EQ_FRAGMENTED) */
INT max_event_size_frag = 5 * max_event_size;

/* buffer size to hold events */
INT event_buffer_size = 5 * max_event_size;

/*-- Shared Memory Configuration --*/
#define SHM_NAME "/my_shared_memory"
#define SEM_NAME "/my_semaphore"
#define SHM_DATA_SIZE (1024 * 1024) // 1 MB

// Shared memory and semaphore handles
int shm_fd = -1;
void* shm_ptr = nullptr;
sem_t* semaphore = nullptr;

/*-- Function declarations -----------------------------------------*/

INT frontend_init(void);
INT frontend_exit(void);
INT begin_of_run(INT run_number, char *error);
INT end_of_run(INT run_number, char *error);
INT pause_run(INT run_number, char *error);
INT resume_run(INT run_number, char *error);
INT frontend_loop(void);

INT read_trigger_event(char *pevent, INT off);

INT poll_event(INT source, INT count, BOOL test);
INT interrupt_configure(INT cmd, INT source, POINTER_T adr);

/*-- Equipment list ------------------------------------------------*/

BOOL equipment_common_overwrite = TRUE;

EQUIPMENT equipment[] = {
   {"Data Simulator",              /* equipment name */
      {2, 0,                 /* event ID, trigger mask */
         "SYSTEM",           /* event buffer */
         EQ_POLLED,          /* equipment type */
         0,                  /* event source */
         "MIDAS",            /* format */
         TRUE,               /* enabled */
         RO_RUNNING,           /* and update ODB */
         100,                /* read every 100 ms */
         0,                  /* stop run after this event limit */
         0,                  /* number of sub events */
         TRUE,               /* log history */
         "", "", "",},
      read_trigger_event    /* readout routine */
   },

   {""}
};

/*-- Frontend Init -------------------------------------------------*/

INT frontend_init() {
    // Open shared memory
    shm_fd = shm_open(SHM_NAME, O_RDONLY, 0666);
    if (shm_fd == -1) {
        cm_msg(MERROR, "frontend_init", "Failed to open shared memory: %s", strerror(errno));
        return FE_ERR_HW;
    }

    // Map shared memory
    shm_ptr = mmap(0, SHM_DATA_SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
    if (shm_ptr == MAP_FAILED) {
        cm_msg(MERROR, "frontend_init", "Failed to map shared memory: %s", strerror(errno));
        close(shm_fd);
        return FE_ERR_HW;
    }
    close(shm_fd);  // Close the file descriptor; it's no longer needed

    // Open semaphore
    semaphore = sem_open(SEM_NAME, 0);
    if (semaphore == SEM_FAILED) {
        cm_msg(MERROR, "frontend_init", "Failed to open semaphore: %s", strerror(errno));
        munmap(shm_ptr, SHM_DATA_SIZE);
        return FE_ERR_HW;
    }

    return SUCCESS;
}

/*-- Frontend Exit -------------------------------------------------*/

INT frontend_exit() {
    // Unmap shared memory
    if (shm_ptr != nullptr) {
        munmap(shm_ptr, SHM_DATA_SIZE);
    }

    // Close semaphore
    if (semaphore != nullptr) {
        sem_close(semaphore);
    }

    return SUCCESS;
}

/*-- Begin of Run --------------------------------------------------*/

INT begin_of_run(INT run_number, char *error) {
    return SUCCESS;
}

/*-- End of Run ----------------------------------------------------*/

INT end_of_run(INT run_number, char *error) {
    return SUCCESS;
}

/*-- Pause Run -----------------------------------------------------*/

INT pause_run(INT run_number, char *error) {
    return SUCCESS;
}

/*-- Resume Run ----------------------------------------------------*/

INT resume_run(INT run_number, char *error) {
    return SUCCESS;
}

/*-- Frontend Loop -------------------------------------------------*/

INT frontend_loop() {
    // No action needed here
    return SUCCESS;
}

/*-- Event readout -------------------------------------------------*/

INT poll_event(INT source, INT count, BOOL test) {
    if (test) {
        // For testing, return FALSE to indicate no event
        return FALSE;
    } else {
        // Check if semaphore is available without blocking
        if (sem_trywait(semaphore) == 0) {
            // Semaphore acquired, event is ready
            return 1; // Event is available
        } else {
            if (errno == EAGAIN) {
                // Semaphore not available, no event ready
                return 0;
            } else {
                // An error occurred
                cm_msg(MERROR, "poll_event", "sem_trywait failed: %s", strerror(errno));
                return 0;
            }
        }
    }
}

/*-- Interrupt configuration ---------------------------------------*/

INT interrupt_configure(INT cmd, INT source, POINTER_T adr) {
   switch (cmd) {
   case CMD_INTERRUPT_ENABLE:
      break;
   case CMD_INTERRUPT_DISABLE:
      break;
   case CMD_INTERRUPT_ATTACH:
      break;
   case CMD_INTERRUPT_DETACH:
      break;
   }
   return SUCCESS;
}

/*-- Trigger event routine -----------------------------------------*/

INT read_trigger_event(char *pevent, INT off) {
    char *pdata;

    // Initialize bank structure
    bk_init32(pevent);

    // Create a bank named "CR00" and specify the data type as TID_BYTE
    bk_create(pevent, "CR00", TID_BYTE, (void **)&pdata);

    // Read data from shared memory
    memcpy(pdata, shm_ptr, SHM_DATA_SIZE);

    // Close the bank
    pdata += SHM_DATA_SIZE;
    bk_close(pevent, pdata);

    return bk_size(pevent);
}
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include "midas.h"
#include "mfe.h"
#include <curl/curl.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <semaphore.h>
#include <chrono>
#include <errno.h>

void trigger_update(INT, INT, void*);

/*-- Globals -------------------------------------------------------*/

/* The frontend name (client name) as seen by other MIDAS clients */
const char *frontend_name = "DataSimulator";
/* The frontend file name, don't change it */
const char *frontend_file_name = __FILE__;

/* frontend_loop is called periodically if this variable is TRUE */
BOOL frontend_call_loop = FALSE;

/* a frontend status page is displayed with this frequency in ms */
INT display_period = 0;

/* maximum event size produced by this frontend */
INT max_event_size = 1024 * 1024 * 32;

/* maximum event size for fragmented events (EQ_FRAGMENTED) */
INT max_event_size_frag = 5 * max_event_size;

/* buffer size to hold events */
INT event_buffer_size = 5 * max_event_size;

/*-- Shared Memory Configuration --*/
#define SHM_NAME "/my_shared_memory"
#define SEM_NAME "/my_semaphore"
#define SHM_DATA_SIZE (1024 * 1024) // 1 MB

// Shared memory and semaphore handles
int shm_fd = -1;
void* shm_ptr = nullptr;
sem_t* semaphore = nullptr;

/*-- Function declarations -----------------------------------------*/

INT frontend_init(void);
INT frontend_exit(void);
INT begin_of_run(INT run_number, char *error);
INT end_of_run(INT run_number, char *error);
INT pause_run(INT run_number, char *error);
INT resume_run(INT run_number, char *error);
INT frontend_loop(void);

INT read_trigger_event(char *pevent, INT off);

INT poll_event(INT source, INT count, BOOL test);
INT interrupt_configure(INT cmd, INT source, POINTER_T adr);

/*-- Equipment list ------------------------------------------------*/

BOOL equipment_common_overwrite = TRUE;

EQUIPMENT equipment[] = {
   {"Data Simulator",              /* equipment name */
      {2, 0,                 /* event ID, trigger mask */
         "SYSTEM",           /* event buffer */
         EQ_POLLED,          /* equipment type */
         0,                  /* event source */
         "MIDAS",            /* format */
         TRUE,               /* enabled */
         RO_RUNNING,           /* and update ODB */
         100,                /* read every 100 ms */
         0,                  /* stop run after this event limit */
         0,                  /* number of sub events */
         TRUE,               /* log history */
         "", "", "",},
      read_trigger_event    /* readout routine */
   },

   {""}
};

/*-- Frontend Init -------------------------------------------------*/

INT frontend_init() {
    // Open shared memory
    shm_fd = shm_open(SHM_NAME, O_RDONLY, 0666);
    if (shm_fd == -1) {
        cm_msg(MERROR, "frontend_init", "Failed to open shared memory: %s", strerror(errno));
        return FE_ERR_HW;
    }

    // Map shared memory
    shm_ptr = mmap(0, SHM_DATA_SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
    if (shm_ptr == MAP_FAILED) {
        cm_msg(MERROR, "frontend_init", "Failed to map shared memory: %s", strerror(errno));
        close(shm_fd);
        return FE_ERR_HW;
    }
    close(shm_fd);  // Close the file descriptor; it's no longer needed

    // Open semaphore
    semaphore = sem_open(SEM_NAME, 0);
    if (semaphore == SEM_FAILED) {
        cm_msg(MERROR, "frontend_init", "Failed to open semaphore: %s", strerror(errno));
        munmap(shm_ptr, SHM_DATA_SIZE);
        return FE_ERR_HW;
    }

    return SUCCESS;
}

/*-- Frontend Exit -------------------------------------------------*/

INT frontend_exit() {
    // Unmap shared memory
    if (shm_ptr != nullptr) {
        munmap(shm_ptr, SHM_DATA_SIZE);
    }

    // Close semaphore
    if (semaphore != nullptr) {
        sem_close(semaphore);
    }

    return SUCCESS;
}

/*-- Begin of Run --------------------------------------------------*/

INT begin_of_run(INT run_number, char *error) {
    return SUCCESS;
}

/*-- End of Run ----------------------------------------------------*/

INT end_of_run(INT run_number, char *error) {
    return SUCCESS;
}

/*-- Pause Run -----------------------------------------------------*/

INT pause_run(INT run_number, char *error) {
    return SUCCESS;
}

/*-- Resume Run ----------------------------------------------------*/

INT resume_run(INT run_number, char *error) {
    return SUCCESS;
}

/*-- Frontend Loop -------------------------------------------------*/

INT frontend_loop() {
    // No action needed here
    return SUCCESS;
}

/*-- Event readout -------------------------------------------------*/

INT poll_event(INT source, INT count, BOOL test) {
    if (test) {
        // For testing, return FALSE to indicate no event
        return FALSE;
    } else {
        // Check if semaphore is available without blocking
        if (sem_trywait(semaphore) == 0) {
            // Semaphore acquired, event is ready
            return 1; // Event is available
        } else {
            if (errno == EAGAIN) {
                // Semaphore not available, no event ready
                return 0;
            } else {
                // An error occurred
                cm_msg(MERROR, "poll_event", "sem_trywait failed: %s", strerror(errno));
                return 0;
            }
        }
    }
}

/*-- Interrupt configuration ---------------------------------------*/

INT interrupt_configure(INT cmd, INT source, POINTER_T adr) {
   switch (cmd) {
   case CMD_INTERRUPT_ENABLE:
      break;
   case CMD_INTERRUPT_DISABLE:
      break;
   case CMD_INTERRUPT_ATTACH:
      break;
   case CMD_INTERRUPT_DETACH:
      break;
   }
   return SUCCESS;
}

/*-- Trigger event routine -----------------------------------------*/

INT read_trigger_event(char *pevent, INT off) {
    char *pdata;

    // Initialize bank structure
    bk_init32(pevent);

    // Create a bank named "CR00" and specify the data type as TID_BYTE
    bk_create(pevent, "CR00", TID_BYTE, (void **)&pdata);

    // Read data from shared memory
    memcpy(pdata, shm_ptr, SHM_DATA_SIZE);

    // Close the bank
    pdata += SHM_DATA_SIZE;
    bk_close(pevent, pdata);

    return bk_size(pevent);
}

And this seems to go significantly faster (~10x faster) than the python frontend solution:
4249a06f1666d561f0426177793c83c1.png


25/09/2024 05:03

I wanted to further investigate the difference between my C++ Library and the Xilinx DMA tools perfromance, so I made some plots of sequentual tests:

Custom C++ Library, 256KB transfer, 1,000,000 samples:
f55cb8ed41d9f8d5d69ab03dea147e54.png

Xilinx DMA tools, 256KB transfer, 100,000 samples:
3434e1e8d7df491c9ebbca842509ec98.png

Custom C++ Library, 32MB transfer, 1,000 samples:

Xilinx DMA tools, 32MB transfer, 1,000 samples:
1005918cae50579b718ff511337fe668.png


25/09/2024 05:22

I also smoothed the "ticker" plots and animated them to see the whole data set. I can't put the animation here, but I noticed "average down time" where there are long periods of slowdown on average visible in the plots below. There are also sharp peaks of downtime, visible in the plots above.

f9416a7a1df9b75989508c26169c08c3.png

2a693800d0ef8368cef374e7d373b02c.png

c3978a792c5c9cab075b0f7eb7f596eb.png

8e4fb5be4e6559c64ae0857b5ff90016.png


25/09/2024 05:49

I made a mistake in my first attempt at the shared memory buffer. The C++ script would just read whatever was in the buffer, with no concern if it was a new data or not. This meant the python script could not bottleneck the rate. I made edits so that the C++ script would not read the same data multiple times by adding a "sequence number" header to the data packets:

data_backend.py

import mmap
import posix_ipc
import struct
import numpy as np
import time

SHM_NAME = "/my_shared_memory"
SEM_NAME = "/my_semaphore"
SHM_DATA_SIZE = 1024 * 1024  # 1 MB
HEADER_FORMAT = 'Q'  # Unsigned long long (8 bytes)

# Calculate total size: header + data
TOTAL_SIZE = struct.calcsize(HEADER_FORMAT) + SHM_DATA_SIZE

# Create or open shared memory
shared_mem = posix_ipc.SharedMemory(SHM_NAME, posix_ipc.O_CREAT, size=TOTAL_SIZE)
memory = mmap.mmap(shared_mem.fd, shared_mem.size)
shared_mem.close_fd()

# Create or open semaphore
semaphore = posix_ipc.Semaphore(SEM_NAME, posix_ipc.O_CREAT, initial_value=0)

sequence_number = 0

try:
    while True:
        # Increment the sequence number
        sequence_number += 1

        # Generate or retrieve data
        data = np.random.randint(0, 256, size=SHM_DATA_SIZE, dtype=np.uint8)
        byte_data = data.tobytes()

        # Pack the sequence number
        header = struct.pack(HEADER_FORMAT, sequence_number)

        # Write header and data to shared memory
        memory.seek(0)
        memory.write(header + byte_data)

        # Signal the C++ frontend
        semaphore.release()

        # Control the data generation rate as needed
except KeyboardInterrupt:
    pass
finally:
    # Clean up
    memory.close()
    shared_mem.unlink()
    semaphore.unlink()
import mmap
import posix_ipc
import struct
import numpy as np
import time

SHM_NAME = "/my_shared_memory"
SEM_NAME = "/my_semaphore"
SHM_DATA_SIZE = 1024 * 1024  # 1 MB
HEADER_FORMAT = 'Q'  # Unsigned long long (8 bytes)

# Calculate total size: header + data
TOTAL_SIZE = struct.calcsize(HEADER_FORMAT) + SHM_DATA_SIZE

# Create or open shared memory
shared_mem = posix_ipc.SharedMemory(SHM_NAME, posix_ipc.O_CREAT, size=TOTAL_SIZE)
memory = mmap.mmap(shared_mem.fd, shared_mem.size)
shared_mem.close_fd()

# Create or open semaphore
semaphore = posix_ipc.Semaphore(SEM_NAME, posix_ipc.O_CREAT, initial_value=0)

sequence_number = 0

try:
    while True:
        # Increment the sequence number
        sequence_number += 1

        # Generate or retrieve data
        data = np.random.randint(0, 256, size=SHM_DATA_SIZE, dtype=np.uint8)
        byte_data = data.tobytes()

        # Pack the sequence number
        header = struct.pack(HEADER_FORMAT, sequence_number)

        # Write header and data to shared memory
        memory.seek(0)
        memory.write(header + byte_data)

        # Signal the C++ frontend
        semaphore.release()

        # Control the data generation rate as needed
except KeyboardInterrupt:
    pass
finally:
    # Clean up
    memory.close()
    shared_mem.unlink()
    semaphore.unlink()

frontend.cxx

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <cstdint>
#include "midas.h"
#include "mfe.h"
#include <curl/curl.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <semaphore.h>
#include <chrono>
#include <errno.h>

void trigger_update(INT, INT, void*);

/*-- Globals -------------------------------------------------------*/

/* The frontend name (client name) as seen by other MIDAS clients */
const char *frontend_name = "DataSimulator";
/* The frontend file name, don't change it */
const char *frontend_file_name = __FILE__;

/* frontend_loop is called periodically if this variable is TRUE */
BOOL frontend_call_loop = FALSE;

/* a frontend status page is displayed with this frequency in ms */
INT display_period = 0;

/* maximum event size produced by this frontend */
INT max_event_size = 1024 * 1024 * 32;

/* maximum event size for fragmented events (EQ_FRAGMENTED) */
INT max_event_size_frag = 5 * max_event_size;

/* buffer size to hold events */
INT event_buffer_size = 5 * max_event_size;

/*-- Shared Memory Configuration --*/
#define SHM_NAME "/my_shared_memory"
#define SEM_NAME "/my_semaphore"
#define SHM_DATA_SIZE (1024 * 1024) // 1 MB

// Total size: header + data
#define TOTAL_SIZE (sizeof(uint64_t) + SHM_DATA_SIZE)

// Shared memory and semaphore handles
int shm_fd = -1;
void* shm_ptr = nullptr;
sem_t* semaphore = nullptr;

/*-- Function declarations -----------------------------------------*/

INT frontend_init(void);
INT frontend_exit(void);
INT begin_of_run(INT run_number, char *error);
INT end_of_run(INT run_number, char *error);
INT pause_run(INT run_number, char *error);
INT resume_run(INT run_number, char *error);
INT frontend_loop(void);

INT read_trigger_event(char *pevent, INT off);

INT poll_event(INT source, INT count, BOOL test);
INT interrupt_configure(INT cmd, INT source, POINTER_T adr);

/*-- Equipment list ------------------------------------------------*/

BOOL equipment_common_overwrite = TRUE;

EQUIPMENT equipment[] = {
   {"Data Simulator",              /* equipment name */
      {2, 0,                 /* event ID, trigger mask */
         "SYSTEM",           /* event buffer */
         EQ_POLLED,          /* equipment type */
         0,                  /* event source */
         "MIDAS",            /* format */
         TRUE,               /* enabled */
         RO_RUNNING,         /* read when running */
         0,                  /* read every event (0 for ASAP) */
         0,                  /* stop run after this event limit */
         0,                  /* number of sub events */
         TRUE,               /* log history */
         "", "", "",},
      read_trigger_event    /* readout routine */
   },

   {""}
};

/*-- Global Variables ----------------------------------------------*/

uint64_t last_sequence_number = 0;

/*-- Frontend Init -------------------------------------------------*/

INT frontend_init() {
    // Open shared memory
    shm_fd = shm_open(SHM_NAME, O_RDONLY, 0666);
    if (shm_fd == -1) {
        cm_msg(MERROR, "frontend_init", "Failed to open shared memory: %s", strerror(errno));
        return FE_ERR_HW;
    }

    // Map shared memory
    shm_ptr = mmap(0, TOTAL_SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
    if (shm_ptr == MAP_FAILED) {
        cm_msg(MERROR, "frontend_init", "Failed to map shared memory: %s", strerror(errno));
        close(shm_fd);
        return FE_ERR_HW;
    }
    close(shm_fd);  // Close the file descriptor; it's no longer needed

    // Open semaphore
    semaphore = sem_open(SEM_NAME, 0);
    if (semaphore == SEM_FAILED) {
        cm_msg(MERROR, "frontend_init", "Failed to open semaphore: %s", strerror(errno));
        munmap(shm_ptr, TOTAL_SIZE);
        return FE_ERR_HW;
    }

    return SUCCESS;
}

/*-- Frontend Exit -------------------------------------------------*/

INT frontend_exit() {
    // Unmap shared memory
    if (shm_ptr != nullptr) {
        munmap(shm_ptr, TOTAL_SIZE);
    }

    // Close semaphore
    if (semaphore != nullptr) {
        sem_close(semaphore);
    }

    return SUCCESS;
}

/*-- Begin of Run --------------------------------------------------*/

INT begin_of_run(INT run_number, char *error) {
    // Reset the last sequence number
    last_sequence_number = 0;
    return SUCCESS;
}

/*-- End of Run ----------------------------------------------------*/

INT end_of_run(INT run_number, char *error) {
    return SUCCESS;
}

/*-- Pause Run -----------------------------------------------------*/

INT pause_run(INT run_number, char *error) {
    return SUCCESS;
}

/*-- Resume Run ----------------------------------------------------*/

INT resume_run(INT run_number, char *error) {
    return SUCCESS;
}

/*-- Frontend Loop -------------------------------------------------*/

INT frontend_loop() {
    // No action needed here
    return SUCCESS;
}

/*-- Event readout -------------------------------------------------*/

INT poll_event(INT source, INT count, BOOL test) {
    if (test) {
        // For testing, return FALSE to indicate no event
        return FALSE;
    } else {
        // Check if semaphore is available without blocking
        if (sem_trywait(semaphore) == 0) {
            // Semaphore acquired, event is ready

            // Read the sequence number from shared memory
            uint64_t sequence_number = *(reinterpret_cast<uint64_t*>(shm_ptr));

            if (sequence_number != last_sequence_number) {
                // New data is available
                last_sequence_number = sequence_number;
                return 1; // Event is available
            } else {
                // Duplicate data, ignore
                return 0;
            }
        } else {
            if (errno == EAGAIN) {
                // Semaphore not available, no event ready
                return 0;
            } else {
                // An error occurred
                cm_msg(MERROR, "poll_event", "sem_trywait failed: %s", strerror(errno));
                return 0;
            }
        }
    }
}

/*-- Interrupt configuration ---------------------------------------*/

INT interrupt_configure(INT cmd, INT source, POINTER_T adr) {
   switch (cmd) {
   case CMD_INTERRUPT_ENABLE:
      break;
   case CMD_INTERRUPT_DISABLE:
      break;
   case CMD_INTERRUPT_ATTACH:
      break;
   case CMD_INTERRUPT_DETACH:
      break;
   }
   return SUCCESS;
}

/*-- Trigger event routine -----------------------------------------*/

INT read_trigger_event(char *pevent, INT off) {
    char *pdata;

    // Initialize bank structure
    bk_init32(pevent);

    // Create a bank named "CR00" and specify the data type as TID_BYTE
    bk_create(pevent, "CR00", TID_BYTE, (void **)&pdata);

    // Copy data from shared memory (excluding the sequence number)
    memcpy(pdata, (char*)shm_ptr + sizeof(uint64_t), SHM_DATA_SIZE);

    // Close the bank
    pdata += SHM_DATA_SIZE;
    bk_close(pevent, pdata);

    return bk_size(pevent);
}
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <cstdint>
#include "midas.h"
#include "mfe.h"
#include <curl/curl.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <semaphore.h>
#include <chrono>
#include <errno.h>

void trigger_update(INT, INT, void*);

/*-- Globals -------------------------------------------------------*/

/* The frontend name (client name) as seen by other MIDAS clients */
const char *frontend_name = "DataSimulator";
/* The frontend file name, don't change it */
const char *frontend_file_name = __FILE__;

/* frontend_loop is called periodically if this variable is TRUE */
BOOL frontend_call_loop = FALSE;

/* a frontend status page is displayed with this frequency in ms */
INT display_period = 0;

/* maximum event size produced by this frontend */
INT max_event_size = 1024 * 1024 * 32;

/* maximum event size for fragmented events (EQ_FRAGMENTED) */
INT max_event_size_frag = 5 * max_event_size;

/* buffer size to hold events */
INT event_buffer_size = 5 * max_event_size;

/*-- Shared Memory Configuration --*/
#define SHM_NAME "/my_shared_memory"
#define SEM_NAME "/my_semaphore"
#define SHM_DATA_SIZE (1024 * 1024) // 1 MB

// Total size: header + data
#define TOTAL_SIZE (sizeof(uint64_t) + SHM_DATA_SIZE)

// Shared memory and semaphore handles
int shm_fd = -1;
void* shm_ptr = nullptr;
sem_t* semaphore = nullptr;

/*-- Function declarations -----------------------------------------*/

INT frontend_init(void);
INT frontend_exit(void);
INT begin_of_run(INT run_number, char *error);
INT end_of_run(INT run_number, char *error);
INT pause_run(INT run_number, char *error);
INT resume_run(INT run_number, char *error);
INT frontend_loop(void);

INT read_trigger_event(char *pevent, INT off);

INT poll_event(INT source, INT count, BOOL test);
INT interrupt_configure(INT cmd, INT source, POINTER_T adr);

/*-- Equipment list ------------------------------------------------*/

BOOL equipment_common_overwrite = TRUE;

EQUIPMENT equipment[] = {
   {"Data Simulator",              /* equipment name */
      {2, 0,                 /* event ID, trigger mask */
         "SYSTEM",           /* event buffer */
         EQ_POLLED,          /* equipment type */
         0,                  /* event source */
         "MIDAS",            /* format */
         TRUE,               /* enabled */
         RO_RUNNING,         /* read when running */
         0,                  /* read every event (0 for ASAP) */
         0,                  /* stop run after this event limit */
         0,                  /* number of sub events */
         TRUE,               /* log history */
         "", "", "",},
      read_trigger_event    /* readout routine */
   },

   {""}
};

/*-- Global Variables ----------------------------------------------*/

uint64_t last_sequence_number = 0;

/*-- Frontend Init -------------------------------------------------*/

INT frontend_init() {
    // Open shared memory
    shm_fd = shm_open(SHM_NAME, O_RDONLY, 0666);
    if (shm_fd == -1) {
        cm_msg(MERROR, "frontend_init", "Failed to open shared memory: %s", strerror(errno));
        return FE_ERR_HW;
    }

    // Map shared memory
    shm_ptr = mmap(0, TOTAL_SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
    if (shm_ptr == MAP_FAILED) {
        cm_msg(MERROR, "frontend_init", "Failed to map shared memory: %s", strerror(errno));
        close(shm_fd);
        return FE_ERR_HW;
    }
    close(shm_fd);  // Close the file descriptor; it's no longer needed

    // Open semaphore
    semaphore = sem_open(SEM_NAME, 0);
    if (semaphore == SEM_FAILED) {
        cm_msg(MERROR, "frontend_init", "Failed to open semaphore: %s", strerror(errno));
        munmap(shm_ptr, TOTAL_SIZE);
        return FE_ERR_HW;
    }

    return SUCCESS;
}

/*-- Frontend Exit -------------------------------------------------*/

INT frontend_exit() {
    // Unmap shared memory
    if (shm_ptr != nullptr) {
        munmap(shm_ptr, TOTAL_SIZE);
    }

    // Close semaphore
    if (semaphore != nullptr) {
        sem_close(semaphore);
    }

    return SUCCESS;
}

/*-- Begin of Run --------------------------------------------------*/

INT begin_of_run(INT run_number, char *error) {
    // Reset the last sequence number
    last_sequence_number = 0;
    return SUCCESS;
}

/*-- End of Run ----------------------------------------------------*/

INT end_of_run(INT run_number, char *error) {
    return SUCCESS;
}

/*-- Pause Run -----------------------------------------------------*/

INT pause_run(INT run_number, char *error) {
    return SUCCESS;
}

/*-- Resume Run ----------------------------------------------------*/

INT resume_run(INT run_number, char *error) {
    return SUCCESS;
}

/*-- Frontend Loop -------------------------------------------------*/

INT frontend_loop() {
    // No action needed here
    return SUCCESS;
}

/*-- Event readout -------------------------------------------------*/

INT poll_event(INT source, INT count, BOOL test) {
    if (test) {
        // For testing, return FALSE to indicate no event
        return FALSE;
    } else {
        // Check if semaphore is available without blocking
        if (sem_trywait(semaphore) == 0) {
            // Semaphore acquired, event is ready

            // Read the sequence number from shared memory
            uint64_t sequence_number = *(reinterpret_cast<uint64_t*>(shm_ptr));

            if (sequence_number != last_sequence_number) {
                // New data is available
                last_sequence_number = sequence_number;
                return 1; // Event is available
            } else {
                // Duplicate data, ignore
                return 0;
            }
        } else {
            if (errno == EAGAIN) {
                // Semaphore not available, no event ready
                return 0;
            } else {
                // An error occurred
                cm_msg(MERROR, "poll_event", "sem_trywait failed: %s", strerror(errno));
                return 0;
            }
        }
    }
}

/*-- Interrupt configuration ---------------------------------------*/

INT interrupt_configure(INT cmd, INT source, POINTER_T adr) {
   switch (cmd) {
   case CMD_INTERRUPT_ENABLE:
      break;
   case CMD_INTERRUPT_DISABLE:
      break;
   case CMD_INTERRUPT_ATTACH:
      break;
   case CMD_INTERRUPT_DETACH:
      break;
   }
   return SUCCESS;
}

/*-- Trigger event routine -----------------------------------------*/

INT read_trigger_event(char *pevent, INT off) {
    char *pdata;

    // Initialize bank structure
    bk_init32(pevent);

    // Create a bank named "CR00" and specify the data type as TID_BYTE
    bk_create(pevent, "CR00", TID_BYTE, (void **)&pdata);

    // Copy data from shared memory (excluding the sequence number)
    memcpy(pdata, (char*)shm_ptr + sizeof(uint64_t), SHM_DATA_SIZE);

    // Close the bank
    pdata += SHM_DATA_SIZE;
    bk_close(pevent, pdata);

    return bk_size(pevent);
}

5f67e68721318e1d055fca24de119680.png


26/09/2024 05:59

I tried a few things to get the "correct shape" of the transfer rate curve using that we see using Xilinx XDMA tools (as seen in the figure below)
57fd28d41c4370545ab7c345ebb4f82c.png

1. Writing the script solely in C
65c866fe4d18937ac91f4346d42bddfc.png

I made a few more testing smaller things:

second data set:
5767b513407f694ce56dad52f6d00bfd.png

100 trials per data point:
42fcfdde717770881a561ee71bb7a486.png

500 trials per data point, adjusted makefile compiler flags to match Xilinx's makefile:
c1e8f2fce6ecd2e53662e81b1245a385.png

100 trials per data point, a delay added in the code to allow for "recovery":
015d212307434137041f78ae71400ac0.png

2. Optimizing the C++ library
I made a branch of my C++ Library that "optimizes" the code a bit; instead using pointers to avoid any sort of overhead. However, I still did not see the expected "shape"

b245fc4288b3515fc302537c73c0f481.png